package fr.enseirb.collabs.rest;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

import fr.enseirb.collabs.model.Collaborator;
import fr.enseirb.collabs.model.Profile;
import fr.enseirb.collabs.model.Skill;
import fr.enseirb.collabs.persistence.MemoryDataBundle;

@Path("/collabs")
public class CollabsREST {

	private MemoryDataBundle model;
	private ObjectMapper mapper;

	public CollabsREST() {
		mapper = new ObjectMapper();
		model = MemoryDataBundle.getInstance();
	}

	// //////////////////////
	// GET METHODS : QUERY
	// //////////////////////

	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public String getAllCollabs() {
		try {
			return mapper.writeValueAsString(model.getCollabs());
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	@GET
	@Path("{id}")
	@Produces(MediaType.APPLICATION_JSON)
	public String getCollab(@PathParam("id") int id) {
		try {
			return mapper.writeValueAsString(model.getCollabByID(id));
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	@GET
	@Path("{id}/profile")
	@Produces(MediaType.APPLICATION_JSON)
	public String getCollabProfile(@PathParam("id") int id) {
		try {
			return mapper.writeValueAsString(model.getCollabByID(id)
					.getProfile());
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	@GET
	@Path("{id}/skills")
	@Produces(MediaType.APPLICATION_JSON)
	public String getCollabSkills(@PathParam("id") int id) {
		try {
			Collaborator collab = model.getCollabByID(id);
			if (collab == null) {
				return mapper.writeValueAsString(false);
			}
			List<Integer> skillsID = collab.getSkills();
			List<Skill> skills = new ArrayList<Skill>();
			for (int skillID : skillsID) {
				Skill s = model.getSkillByID(skillID);
				if (s == null) {
					continue;
				}
				skills.add(s);
			}
			return mapper.writeValueAsString(skills);
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	private List<Integer> getSkillsIDsInParam(UriInfo uri) {

		// Retrieves GET params
		MultivaluedMap<String, String> params = uri.getQueryParameters();
		List<Integer> skills = new ArrayList<Integer>();
		String param = params.get("skills").get(0);
		String[] ids = param.split("\\s");
		for (String id : ids) {
			skills.add(Integer.parseInt(id));
		}

		return skills;
	}

	@GET
	@Path("query/all")
	@Produces(MediaType.APPLICATION_JSON)
	public String queryOnCollabsAll(@Context UriInfo allUri) {

		try {
			List<Integer> skills = getSkillsIDsInParam(allUri);

			// Search
			List<Collaborator> result = new ArrayList<Collaborator>();
			Collection<Collaborator> collabs = model.getCollabs();
			for (Collaborator collab : collabs) {
				if (collab.getSkills().containsAll(skills)) {
					result.add(collab);
				}
			}

			return mapper.writeValueAsString(result);
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}

	@GET
	@Path("query/oneOf")
	@Produces(MediaType.APPLICATION_JSON)
	public String queryOnCollabsOneOf(@Context UriInfo allUri) {

		try {
			List<Integer> skills = getSkillsIDsInParam(allUri);

			// Search
			List<Collaborator> result = new ArrayList<Collaborator>();
			Collection<Collaborator> collabs = model.getCollabs();
			for (Collaborator collab : collabs) {
				for (Integer id : skills) {
					if (collab.getSkills().contains(id)) {
						result.add(collab);
						break;
					}
				}
			}

			return mapper.writeValueAsString(result);
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}

	// //////////////////////
	// POST METHODS : ADD
	// //////////////////////

	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public String addCollaborator(String json) {

		try {
			Collaborator collab = mapper.readValue(json, Collaborator.class);
			if (collab == null) {
				return mapper.writeValueAsString(new Boolean(false));
			}
			model.addCollaborator(collab);
			return mapper.writeValueAsString(new Boolean(true));
		} catch (JsonParseException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}

	@POST
	@Path("{collab_id}/skills/{skill_id}")
	@Produces(MediaType.APPLICATION_JSON)
	public String addSkillToCollaborator(@PathParam("collab_id") int collabId,
			@PathParam("skill_id") int skillId) {

		try {
			Collaborator collab = model.getCollabByID(collabId);
			if (collab == null) {
				return mapper.writeValueAsString(new Boolean(false));
			}
			Skill skill = model.getSkillByID(skillId);
			if (skill == null) {
				return mapper.writeValueAsString(new Boolean(false));
			}
			collab.addSkill(skill.getId());
			return mapper.writeValueAsString(new Boolean(true));
		} catch (JsonParseException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}

	// //////////////////////
	// PUT METHODS : EDIT
	// //////////////////////

	@PUT
	@Path("{id}/profile")
	@Produces(MediaType.APPLICATION_JSON)
	public String editCollabProfile(@PathParam("id") int id, String json) {
		try {
			Collaborator collab = model.getCollabByID(id);
			if (collab == null) {
				return mapper.writeValueAsString(new Boolean(false));
			}
			Profile profile = mapper.readValue(json, Profile.class);
			if (profile == null) {
				return mapper.writeValueAsString(new Boolean(false));
			}
			collab.setProfile(profile);
			return mapper.writeValueAsString(new Boolean(true));
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	// //////////////////////
	// DELETE METHODS : DELETE
	// //////////////////////

	@DELETE
	@Path("{id}")
	@Produces(MediaType.APPLICATION_JSON)
	public String deleteCollab(@PathParam("id") int id) {

		try {
			if (model.deleteCollaborator(id) == true) {
				return mapper.writeValueAsString(new Boolean(true));
			} else {
				return mapper.writeValueAsString(new Boolean(false));
			}
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}

	@DELETE
	@Path("{collab_id}/skills/{skill_id}")
	@Produces(MediaType.APPLICATION_JSON)
	public String deleteSkillFromCollaborator(
			@PathParam("collab_id") int collabId,
			@PathParam("skill_id") int skillId) {

		try {
			Collaborator collab = model.getCollabByID(collabId);
			if (collab == null) {
				return mapper.writeValueAsString(new Boolean(false));
			}
			Skill skill = model.getSkillByID(skillId);
			if (skill == null) {
				return mapper.writeValueAsString(new Boolean(false));
			}
			collab.removeSkill(skill.getId());
			return mapper.writeValueAsString(new Boolean(true));
		} catch (JsonParseException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}
}
